package main

// 初始化 一个线程安全的map，存储一个产品有100个库存。
// 启动N个线程 ，从 order chan中取数据，处理之后 从map对应的商品 库存数据-1，chan取不到数据就阻塞
// 用户抢购，并发多个用户抢购， 创建order写入 order chan，chan满了就失败。
import (
	"fmt"
	"sync"
	"time"
)

type SeckillService struct {
	inventory sync.Map // 商品库存
	orderChan chan *Order
	wg        sync.WaitGroup
}

type Order struct {
	UserID    string
	ProductID string
	Quantity  int
}

func NewSeckillService() *SeckillService {
	s := &SeckillService{
		orderChan: make(chan *Order, 10), // 缓冲订单通道
	}

	// 初始化库存
	s.inventory.Store("product_001", 10) // 10件商品

	// 启动订单处理worker
	for i := 0; i < 5; i++ {
		go s.orderProcessor(i)
	}

	return s
}

// 订单处理线程， 提高应用并发/吞吐能力
func (s *SeckillService) orderProcessor(workerID int) {
	for order := range s.orderChan { //chan没数据会阻塞，直到chan被关闭
		// 模拟订单处理
		time.Sleep(10 * time.Millisecond)
		fmt.Printf("Worker%d处理订单: 用户%s购买%s\n", workerID, order.UserID, order.ProductID)
		if !s.checkInventory(order.ProductID) {
			fmt.Printf("Worker%d处理订单: 用户%s购买%s, 库存不足\n", workerID, order.UserID, order.ProductID)
		} else {
			// 扣减库存
			s.decrementInventory(order.ProductID)
		}

	}
}

// 用户抢购请求
func (s *SeckillService) SeckillRequest(userID, productID string) {
	s.wg.Add(1)
	go func() {
		defer s.wg.Done()

		// 检查库存
		if !s.checkInventory(productID) {
			fmt.Printf("用户%s: 库存不足\n", userID)
			return
		}

		// 创建订单
		order := &Order{
			UserID:    userID,
			ProductID: productID,
			Quantity:  1,
		}

		// 发送到订单处理通道 select语句会同时监控多个 case 分支，执行其中第一个就绪的 case
		select {
		case s.orderChan <- order:
			fmt.Printf("用户%s: 抢购成功\n", userID)
		case <-time.After(100 * time.Millisecond):
			fmt.Printf("用户%s: 系统繁忙\n", userID)
		}
	}()
}

// 是否还有库存
func (s *SeckillService) checkInventory(productID string) bool {
	value, ok := s.inventory.Load(productID) //sync.map的Load方法是有锁的
	if !ok {
		return false
	}
	fmt.Printf(" 库存:%d\n", value)
	inventory := value.(int)
	return inventory > 0
}

// 扣减库存
func (s *SeckillService) decrementInventory(productID string) {
	//sycnc.map 是线程安全的锁，Store方法会加锁
	value, ok := s.inventory.Load(productID)
	if !ok {
		return
	}
	inventory := value.(int)
	if inventory > 0 {
		s.inventory.Store(productID, inventory-1)
	}
}

func main() {
	service := NewSeckillService()

	// 模拟20个用户同时抢购
	for i := 0; i < 20; i++ {
		userID := fmt.Sprintf("user_%04d", i)
		service.SeckillRequest(userID, "product_001")
		//每个seckillRequest 会创建wg.Add(1)，在请求处理完成后wg.Done()
	}

	service.wg.Wait() //等待所有SeckillRequest 都完成
	close(service.orderChan)
	time.Sleep(2 * time.Second)
}
